//
//  PhotoSingle.swift
//  photoTool
//
//  Created by 刘清元 on 2022/8/27.
//

import UIKit
import Photos
import WCDBSwift
import Vision

class PhotoSingle: NSObject,@unchecked Sendable {
    @MainActor static let shared = PhotoSingle()

    // 重复图像名称
    var repeatPhotoListArr: [Int] = []
    // 是否已经扫描
    var didScan = false
    // 已保存图片数量
    var didSave = 0
    // 相册数组
    var assetsArr: [PHAsset] = []

    // 下载进度
    private var scanProgressBlock: (() -> ())?

    func scanProgess(_ closure: @escaping () -> Void) -> Self {
        scanProgressBlock = closure
        return self
    }

    // 锁
    private var lock = NSLock()
    // 下载完成
    private var scanCompleteBlock: (() -> ())?

    func scanComplete(_ closure: @escaping () -> Void) {
        scanTimer.invalidate()
        scanCompleteBlock = closure
    }

    private lazy var scanTimer: Timer = {
        let time = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
            if self.scanProgressBlock != nil, !self.assetsArr.isEmpty {
                DispatchQueue.main.async {
                    self.scanProgressBlock!()
                }
            }
        }
        return time
    }()

    // 相册权限
    func registerPhoto() -> Self {
        assetsArr = []
        if #available(iOS 14, *) {
            PHPhotoLibrary.requestAuthorization(for: .readWrite) { status in
                switch status {
                case .notDetermined:
                    print("拒绝")
                case .restricted:
                    print("rest")
                case .denied:
                    print("拒绝")
                case .authorized:
                    self.scanPhoto()
                case .limited:
                    self.scanPhoto()
                @unknown default:
                    print("default")
                }
            }
        } else {
            PHPhotoLibrary.requestAuthorization { status in
                switch status {
                case .notDetermined:
                    print("拒绝")
                case .restricted:
                    print("rest")
                case .denied:
                    print("拒绝")
                case .authorized:
                    self.scanPhoto()
                case .limited:
                    self.scanPhoto()
                @unknown default:
                    print("default")
                }
            }
        }
        return self
    }

    func scanPhoto() {
        // 扫描图片，并保存到数据库中
        let option = PHFetchOptions.init()
        print("开始获取所有图片")
        option.sortDescriptors = [NSSortDescriptor.init(key: "creationDate", ascending: false)]
        let result = PHAsset.fetchAssets(with: .image, options: option)
        result.enumerateObjects { obj, index, stop in
            self.assetsArr.append(obj)
        }
        print("获取全部图片结束")
        savePhoto(album: "")
    }

    // 保存相册
    func savePhoto(album:String) {
        // 保存图片
        // 重置数据为删除状态
        let model = ImageModel()
        model.isDelete = 1
        if album.isEmpty {
            try! FileTool().getDb().update(table: "image", on: ImageModel.Properties.isDelete, with: model)
        }else{
            try! FileTool().getDb().update(table: "image", on: ImageModel.Properties.isDelete, with: model, where: ImageModel.Properties.groupName == album)
        }
        if assetsArr.isEmpty {
            if self.scanCompleteBlock != nil {
                self.scanCompleteBlock!()
            }
            return
        }
        if assetsArr.count > 1 {
            let middleNum: Int = assetsArr.count / 2
            let group = DispatchGroup()
            for item in 0...1 {
                let queue = DispatchQueue.init(label: "scanPhotoQueue\(item)")
                queue.async(group: group) {
                    for index in item == 0 ? (0...middleNum) : (middleNum + 1...self.assetsArr.count - 1) {
                        let photoAsset = self.assetsArr[index]
                        // 先从数据库中查询，是否存在当前数据
                        let modelArr:[ImageModel] = try! FileTool().getDb().getObjects(fromTable: "image",where: ImageModel.Properties.localIdentifier == photoAsset.localIdentifier)
                        var imageModel = ImageModel.init()
                        if !modelArr.isEmpty {
                            imageModel = modelArr.first!
                        }
                        let fileName = photoAsset.value(forKey: "filename") as! String
                        imageModel.name = fileName
                        imageModel.width = photoAsset.value(forKey: "pixelWidth") as? Int
                        imageModel.height = photoAsset.value(forKey: "pixelHeight") as? Int
                        let phAssets = PHAssetResource.assetResources(for: photoAsset).first
                        if let size = phAssets?.value(forKey: "fileSize") {
                            imageModel.fileSize = size as? Int
                        }
                        imageModel.groupName = ""
                        if !album.isEmpty {
                            imageModel.groupName = album
                        }
                        imageModel.isDelete = 0
                        if modelArr.isEmpty {
                            imageModel.hasContrast = 0
                        }
                        imageModel.localIdentifier = photoAsset.localIdentifier
                        let bili:Double = Double(imageModel.height!)/Double(imageModel.width!)
                        let biliStr = String(format: "%.3f", bili)
                        imageModel.bili = Double(biliStr)!
                        if modelArr.isEmpty {
                            // 不存在同样数据，从新插入
                            try! FileTool().getDb().insert(imageModel, intoTable: "image")
                        }else{
                            // 存在同样数据，更新旧数据
                            try! FileTool().getDb().update(table: "image",
                                                           on: [
                                                            ImageModel.Properties.name,
                                                            ImageModel.Properties.width,
                                                            ImageModel.Properties.height,
                                                            ImageModel.Properties.groupName,
                                                            ImageModel.Properties.isDelete,
                                                            ImageModel.Properties.localIdentifier,
                                                            ImageModel.Properties.bili
                                                           ],
                                                           with: imageModel,where: ImageModel.Properties.localIdentifier == imageModel.localIdentifier!
                            )
                        }
                        self.lock.lock()
                        self.didSave += 1
                        self.lock.unlock()
                        if self.didSave % 100 == 0 {
                            print("已扫描\(self.didSave)")
                        }
                    }
                }
            }
            group.notify(queue: DispatchQueue.main) {
                self.didScan = true
                self.removeDeleteImage(album: album)
                self.assetsArr = []
                self.didSave = 0
                if self.scanCompleteBlock != nil {
                    self.scanCompleteBlock!()
                }
            }
        } else {
            let photoAsset = self.assetsArr.first!
            // 先从数据库中查询，是否存在当前数据
            let modelArr:[ImageModel] = try! FileTool().getDb().getObjects(fromTable: "image",where: ImageModel.Properties.localIdentifier == photoAsset.localIdentifier)
            var imageModel = ImageModel.init()
            if !modelArr.isEmpty {
                imageModel = modelArr.first!
            }
            let fileName = photoAsset.value(forKey: "filename") as! String
            imageModel.name = fileName
            imageModel.width = photoAsset.value(forKey: "pixelWidth") as? Int
            imageModel.height = photoAsset.value(forKey: "pixelHeight") as? Int
            let phAssets = PHAssetResource.assetResources(for: photoAsset).first
            if let size = phAssets?.value(forKey: "fileSize") {
                imageModel.fileSize = size as? Int
            }
            imageModel.groupName = ""
            if !album.isEmpty {
                imageModel.groupName = album
            }
            imageModel.isDelete = 0
            if modelArr.isEmpty {
                imageModel.hasContrast = 0
            }
            imageModel.localIdentifier = photoAsset.localIdentifier
            let bili:Double = Double(imageModel.height!)/Double(imageModel.width!)
            let biliStr = String(format: "%.3f", bili)
            imageModel.bili = Double(biliStr)!
            if modelArr.isEmpty {
                // 不存在同样数据，从新插入
                try! FileTool().getDb().insert(imageModel, intoTable: "image")
            }else{
                // 存在同样数据，更新旧数据
                try! FileTool().getDb().update(table: "image",
                                               on: [
                                                ImageModel.Properties.name,
                                                ImageModel.Properties.width,
                                                ImageModel.Properties.height,
                                                ImageModel.Properties.groupName,
                                                ImageModel.Properties.isDelete,
                                                ImageModel.Properties.localIdentifier,
                                                ImageModel.Properties.bili
                                               ],
                                               with: imageModel,where: ImageModel.Properties.localIdentifier == imageModel.localIdentifier!
                )
            }
            self.didScan = true
            self.removeDeleteImage(album: album)
            self.assetsArr = []
            if self.scanCompleteBlock != nil {
                self.scanCompleteBlock!()
            }
        }
        
    }
    // MARK: 删除状态为已删除的本地数据
    func removeDeleteImage(album:String) {
        let fileTool = FileTool()
        var allModel:[ImageModel] = []
        if album.isEmpty {
            allModel = try! FileTool().getDb().getObjects(fromTable: "image",where: ImageModel.Properties.isDelete == 1)
        }else{
            allModel = try! FileTool().getDb().getObjects(fromTable: "image",where: ImageModel.Properties.isDelete == 1 && ImageModel.Properties.groupName == album)
        }
        let filePath = fileTool.getDocumentPath()
        for item in allModel {
            _ = fileTool.deleteFileWithPath(path: filePath + "/imageData/" + item.localIdentifier!.md5)
//            UserDefaults.standard.removeObject(forKey: item.localIdentifier!)
            try! FileTool().getDb().delete(fromTable: "repeated",where: RepeatedModel.Properties.leftImgId == item.localIdentifier! || RepeatedModel.Properties.rightImgId == item.localIdentifier!)
            try! FileTool().getDb().delete(fromTable: "image",where: ImageModel.Properties.localIdentifier == item.localIdentifier!)
        }
    }
    
    func getAllImgList() -> [ImageModel] {
        try! FileTool().getDb().getObjects(fromTable: "image")
    }
    // 获取所有相册
    func getAllAlbum(success:((_ list:[AlbumModel])->())){
        var dataArray:[AlbumModel] = []
        let albums = PHAssetCollection.fetchAssetCollections(with: PHAssetCollectionType.album, subtype: PHAssetCollectionSubtype.any, options: nil)
        albums.enumerateObjects { (collection, index, pointer) in
            if collection.isKind(of: PHAssetCollection.self) {
                let fetchResult = PHAsset.fetchAssets(in: collection, options: nil)
                let model = AlbumModel.init()
                model.albumName = collection.localizedTitle!
                model.firstImage = UIImage.init()
                model.collection = collection
                model.fetchResult = fetchResult
                dataArray.append(model)
            }
        }
        success(dataArray)
    }
    
    func scanImage(assetCollection:PHAssetCollection)->Self{
        let options = PHImageRequestOptions.init()
        options.isSynchronous = true
        let assets = PHAsset.fetchAssets(in: assetCollection, options: nil)
        assets.enumerateObjects { obj, index, stop in
            self.assetsArr.append(obj)
        }
        savePhoto(album: assetCollection.value(forKey: "title") as! String)
        return self
    }
    
    // 图片转heic
    func changeJPGToHeic(orignImage:UIImage,filePath:String)->UIImage{
        let image = CIImage(image: orignImage)
        let context = CIContext()
        let heicUrl = URL.init(fileURLWithPath: filePath)
        let options = NSDictionary(dictionary: [kCGImageDestinationLossyCompressionQuality:0.75])
        try! context.writeHEIFRepresentation(of: image!, to: heicUrl, format: CIFormat.ARGB8, colorSpace: (image?.colorSpace)!,options: options as! [CIImageRepresentationOption : Any])
        let heicImage = UIImage.init(contentsOfFile: filePath)
        return heicImage!
    }
    //MARK: 从相册中读取一张图片
    func getImageFromAlbum(albumModel:AlbumModel)->(result:Bool,image:UIImage,asset:PHAsset){
        let options = PHImageRequestOptions.init()
        options.isSynchronous = true
        let assets = PHAsset.fetchAssets(in: albumModel.collection!, options: nil)
        if let assetObjc = assets.firstObject {
            var resultImage = UIImage.init()
            let signal = DispatchSemaphore(value: 0)
            PHImageManager.default().requestImageData(for: assetObjc, options: options) { imageData, dataUTI, orientation, info in
                let image = UIImage.init(data: imageData!)
                resultImage = image!
                signal.signal()
            }
            signal.wait()
            return (result:true,image:resultImage,asset:assetObjc)
        }else{
            return (result:false,image:UIImage.init(),asset:PHAsset())
        }
    }
    //MARK: 从相册中读取多张图片
    func getImagesFromAlbum(albumModel:AlbumModel,imageCount:Int)->[ImageModel]{
        let options = PHImageRequestOptions.init()
        options.isSynchronous = true
        let assets = PHAsset.fetchAssets(in: albumModel.collection!, options: nil)
        var imageArr:[ImageModel] = []
        if assets.count > imageCount {
            assets.enumerateObjects { asset, index, stop in
                if index >= imageCount {
                    return
                }
                let model = ImageModel.init()
                let fileName = asset.value(forKey: "filename") as! String
                model.name = fileName
                model.localIdentifier = asset.localIdentifier
                imageArr.append(model)
            }
        }else{
            assets.enumerateObjects { asset, index, stop in
                let model = ImageModel.init()
                    let fileName = asset.value(forKey: "filename") as! String
                    model.name = fileName
                    model.localIdentifier = asset.localIdentifier
                    imageArr.append(model)
            }
        }
        return imageArr
    }
    //MARK: 从相册中删除一张图片
    func deleteImageFromAlbum(albumModel:AlbumModel,asset:PHAsset)->Bool{
        let signal = DispatchSemaphore(value: 0)
        PHPhotoLibrary.shared().performChanges {
            let albumChangeRequest = PHAssetCollectionChangeRequest(for: albumModel.collection!)
            albumChangeRequest?.removeAssets([asset] as NSFastEnumeration)
        } completionHandler: { result, error in
            signal.signal()
        }
        signal.wait()
        return true
    }
    //MARK: 从相册中删除多张图片
    func deleteImagesFromAlbum(albumModel:AlbumModel,assets:[PHAsset])->Bool{
        let signal = DispatchSemaphore(value: 0)
        PHPhotoLibrary.shared().performChanges {
            let albumChangeRequest = PHAssetCollectionChangeRequest(for: albumModel.collection!)
            albumChangeRequest?.removeAssets(assets as NSFastEnumeration)
        } completionHandler: { result, error in
            signal.signal()
        }
        signal.wait()
        return true
    }
    //MARK: 从相册中添加一张图片
    func insertImageToAlbum(albumModel:AlbumModel,image:UIImage)->Bool{
        let signal = DispatchSemaphore(value: 0)
        PHPhotoLibrary.shared().performChanges {
            let assetChangeRequest = PHAssetChangeRequest.creationRequestForAsset(from: image)
            let assetPlaceholder = assetChangeRequest.placeholderForCreatedAsset
            let albumChangeRequest = PHAssetCollectionChangeRequest(for: albumModel.collection!)
            albumChangeRequest?.addAssets([assetPlaceholder] as NSFastEnumeration)
        } completionHandler: { result, error in
            signal.signal()
        }

        signal.wait()
        return true
    }
    //MARK: 移动某个asset到指定相册
    func moveImageToAlbum(albumModel:AlbumModel,asset:PHAsset)->Bool{
        let signal = DispatchSemaphore(value: 0)
        PHPhotoLibrary.shared().performChanges {
            let albumChangeRequest = PHAssetCollectionChangeRequest(for: albumModel.collection!)
            albumChangeRequest?.addAssets([asset] as NSFastEnumeration)
        } completionHandler: { result, error in
            signal.signal()
        }

        signal.wait()
        return true
    }
    //MARK: 移动多个asset到指定相册
    func moveImagesToAlbum(albumModel:AlbumModel,assets:[PHAsset])->Bool{
        let signal = DispatchSemaphore(value: 0)
        PHPhotoLibrary.shared().performChanges {
            let albumChangeRequest = PHAssetCollectionChangeRequest(for: albumModel.collection!)
            albumChangeRequest?.addAssets(assets as NSFastEnumeration)
        } completionHandler: { result, error in
            signal.signal()
        }

        signal.wait()
        return true
    }
    // 获取图片特性
    func getImageFeature(localIdentifier:String)->FeatureImageModel{
        var model = FeatureImageModel()
        model.imageId = localIdentifier
        let fileTool = FileTool.init()
        let filePath = fileTool.getDocumentPath()
        let dataFilePath = filePath+"/imageData/"+localIdentifier.md5
        if FileManager.default.fileExists(atPath: dataFilePath) {
            let filePathUrl = URL.init(fileURLWithPath: dataFilePath)
            do {
                let data = try Data.init(contentsOf: filePathUrl)
                let data1 = try NSKeyedUnarchiver.unarchivedObject(ofClass: VNFeaturePrintObservation.self, from: data)
                model.feature = data1!
            } catch let error {
                print(error.localizedDescription)
            }
        }else{
            // 使用Vision获取图片信息
            var assetResult = PHAsset.fetchAssets(withLocalIdentifiers: [localIdentifier], options: nil)
            if let asset = assetResult.firstObject{
                var thumbImage:UIImage? = nil
                let option = PHImageRequestOptions()
                option.isSynchronous = true
                PHImageManager.default().requestImageDataAndOrientation(for: asset, options: option) { (data, _, orientation, info) in
                    if data != nil {
                        thumbImage = UIImage.init(data: data!)
                    }
                }
                assetResult = PHFetchResult()
                let handler = VNImageRequestHandler(cgImage: thumbImage!.cgImage!, orientation: .up)
                let semaphore = DispatchSemaphore(value: 1)
                let request = VNGenerateImageFeaturePrintRequest{ result, error in
                    if error == nil {
                        let r = result as! VNGenerateImageFeaturePrintRequest
                        var requestResult = r.results?.first
                        //将图片信息保存
                        do {
                            var imageResult = try NSKeyedArchiver.archivedData(withRootObject: requestResult as Any, requiringSecureCoding: true)
                            if fileTool.createLocalFile(savePath: filePath+"/imageData", fileName: localIdentifier.md5, fileData: imageResult) {
                                model.feature = requestResult!
                            }else {
                                print("图片信息存储失败")
                            }
                            thumbImage = nil
                            imageResult = Data()
                            requestResult = nil
                            semaphore.signal()
                        } catch let error {
                            print(error.localizedDescription)
                            semaphore.signal()
                        }
                    }else{
                        print("获取图片信息失败")
                    }
                }
                request.revision = VNGenerateImageFeaturePrintRequestRevision1
                try? handler.perform([request])
                semaphore.wait()
            }else{
                print("获取图片失败")
            }
        }
        return model
    }
}

class AlbumModel:@unchecked Sendable {
    var albumName: String = ""
    var firstImage: UIImage?
    var collection: PHAssetCollection?
    var fetchResult: PHFetchResult<PHAsset>?
}
